package com.ls.spring.framework.webmvc.servlet;

import com.ls.spring.framework.annotation.MyRequestParam;
import com.ls.spring.framework.beans.support.MyBeanDefinitionReader;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 挥之以墨
 */
public class MyHandlerAdapter {

    /**
     * 解析某一个方法的形参和返回值，并统一封装为ModelAndView对象
     */
    @SuppressWarnings("unchecked")
    public MyModelAndView handler(HttpServletRequest req, HttpServletResponse resp, MyHandlerMapping handler) throws InvocationTargetException, IllegalAccessException {

        Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();
        // 将controller的形参列表的参数名及位置缓存起来
        Map<String, Integer> paramIndexMapping = createParamIndexMapping(handler);

        // 拼接实参列表，参数重名会出现数组
        Map<String, String[]> parameterMap = req.getParameterMap();
        Object[] paramValues = new Object[parameterTypes.length];
        for (Map.Entry<String, String[]> param : parameterMap.entrySet()) {
            String value = Arrays.toString(param.getValue())
                    .replaceAll("[\\[\\]]", "")
                    .replaceAll("\\s+", ",");
            if (!paramIndexMapping.containsKey(param.getKey())) {
                continue;
            }
            Integer index = paramIndexMapping.get(param.getKey());
            // 通过自定义类型转换器转换
            paramValues[index] = castStringValue(value, parameterTypes[index]);
        }

        if (paramIndexMapping.containsKey(HttpServletRequest.class.getName())) {
            int index = paramIndexMapping.get(HttpServletRequest.class.getName());
            paramValues[index] = req;
        }

        if (paramIndexMapping.containsKey(HttpServletResponse.class.getName())) {
            int index = paramIndexMapping.get(HttpServletResponse.class.getName());
            paramValues[index] = resp;
        }

        Object result = handler.getMethod().invoke(handler.getController(), paramValues);
        if (result == null || result instanceof Void) {
            return null;
        }

        if (handler.getMethod().getReturnType() == MyModelAndView.class) {
            return (MyModelAndView) result;
        }

        // 其它返回格式做转换（此版本暂无）

        return null;
    }

    /**
     * 将controller的形参列表的参数名及位置缓存起来
     */
    private Map<String, Integer> createParamIndexMapping(MyHandlerMapping handler) {
        Map<String, Integer> paramIndexMapping = new HashMap<>(16);

        // 先获取参数列表中的参数名及位置
        Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();
        for (int i = 0; i < parameterTypes.length; i++) {
            Class<?> parameterType = parameterTypes[i];
            if (parameterType == HttpServletRequest.class || parameterType == HttpServletResponse.class) {
                paramIndexMapping.put(parameterType.getName(), i);
            } else {
                paramIndexMapping.put(MyBeanDefinitionReader.toLowerFirstCase(parameterType.getName()), i);
            }
        }

        // 获取注解中的name
        Annotation[][] parameterAnnotations = handler.getMethod().getParameterAnnotations();
        for (int i = 0; i < parameterAnnotations.length; i++) {
            for (Annotation annotation : parameterAnnotations[i]) {
                if (annotation instanceof MyRequestParam) {
                    String value = ((MyRequestParam) annotation).value();
                    if (!"".equals(value)) {
                        paramIndexMapping.put(value, i);
                    }
                }
            }
        }
        return paramIndexMapping;
    }

    /**
     * 自定义类型转换器转换
     */
    private Object castStringValue(String value, Class<?> paramType) {
        if (String.class == paramType) {
            return value;
        } else if (Integer.class == paramType) {
            return Integer.valueOf(value);
        } else if (Double.class == paramType) {
            return Double.valueOf(value);
        } else {
            return value;
        }
    }

}
